package com.microservice.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 基于Redis的分布式锁工具类
 * 用于微服务集群环境下的任务互斥执行
 */
@Slf4j
@Component
public class DistributedLock {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey 锁的键
     * @param value 锁的值（通常使用唯一标识，如UUID）
     * @param expireTime 锁的过期时间
     * @param timeUnit 时间单位
     * @return 是否成功获取锁
     */
    public boolean tryLock(String lockKey, String value, long expireTime, TimeUnit timeUnit) {
        try {
            Boolean result = stringRedisTemplate.opsForValue()
                    .setIfAbsent(lockKey, value, expireTime, timeUnit);
            return Boolean.TRUE.equals(result);
        } catch (Exception e) {
            log.error("获取分布式锁失败: lockKey={}", lockKey, e);
            return false;
        }
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey 锁的键
     * @param value 锁的值（必须与加锁时的值一致才能释放）
     * @return 是否成功释放锁
     */
    public boolean releaseLock(String lockKey, String value) {
        try {
            String currentValue = stringRedisTemplate.opsForValue().get(lockKey);
            // 只有当前值与传入值相同时才删除，防止误删其他实例的锁
            if (value.equals(currentValue)) {
                Boolean result = stringRedisTemplate.delete(lockKey);
                return Boolean.TRUE.equals(result);
            }
            return false;
        } catch (Exception e) {
            log.error("释放分布式锁失败: lockKey={}", lockKey, e);
            return false;
        }
    }

    /**
     * 执行带锁的任务
     *
     * @param lockKey 锁的键
     * @param expireTime 锁的过期时间
     * @param timeUnit 时间单位
     * @param task 要执行的任务
     * @return 是否成功执行（获取锁失败返回false）
     */
    public boolean executeWithLock(String lockKey, long expireTime, TimeUnit timeUnit, Runnable task) {
        String lockValue = String.valueOf(System.currentTimeMillis());
        boolean locked = tryLock(lockKey, lockValue, expireTime, timeUnit);
        
        if (!locked) {
            log.info("未能获取分布式锁，跳过执行: lockKey={}", lockKey);
            return false;
        }

        try {
            log.info("成功获取分布式锁，开始执行任务: lockKey={}", lockKey);
            task.run();
            return true;
        } finally {
            releaseLock(lockKey, lockValue);
            log.info("释放分布式锁: lockKey={}", lockKey);
        }
    }
}
